<?php

abstract class Workset_Model_Object_Storage_Table_Abstract extends Workset_Model_Object_Storage_Abstract {

	protected $_tableClass = null;

	protected $_tableOptions = array();

	protected $_table = null;

    protected $_limit = 200;

    protected $_countAllRecords = true;

    protected $_countMethod = 'select_count';

	/**
	 * Устанавливает опции объекта.
	 *
	 * Массив возможных опций:
	 *
	 * array(
	 *		tableClass 		=> имя класса таблицы
	 * 		table			=> объект таблицы
	 * 		tableOptions 	=> опции объекта таблицы
	 * 
	 * 		countTotalRecords	=> 	true|false - указывает, нужно ли подсчитывать
	 * 								общее кол-во записей (без limit/offset)
	 *
	 * 		countMethod		=>  способ подсчета,
	 * 							select_count - выполнить запрос COUNT(*),
	 * 							calc_found_rows - подставлять в запрос опцию SQL_CALC_FOUND_ROWS
	 * 							и возвращать кол-во записей функцией FOUND_ROWS()
	 *
	 * )
	 *
	 * @param  $options
	 * @return void
	 */
	function setOptions(array $options) {
		parent::setOptions($options);

		foreach ($this->_options as $key => $option) {
			switch ($key) {
				case 'tableClass':
					$this->setTableClass($option);
					unset($this->_options[$key]);
				break;

				case 'table':
					$this->setTable($option);
					unset($this->_options[$key]);
				break;

				case 'tableOptions':
					$this->setTableOptions($option);
					unset($this->_options[$key]);
				break;

                case 'countTotalRecords':
                    $this->setCountAllRecords($option);
					unset($this->_options[$key]);
                break;

                case 'countMethod':
                    $this->_countMethod = $option;
					unset($this->_options[$key]);
                break;
			}
		}

		$this->_options = $options;
	}

	/**
	 * Устанавливает имя класса таблицы.
	 *
	 * @param  $class
	 * @return Workset_Model_Object_Storage_Table_Abstract
	 */
	function setTableClass($class) {
        $this->_tableClass = $class;
        return $this;
    }

	/**
	 * Возвращает имя класса таблицы.
	 *
	 * @return string
	 */
	function getTableClass() {
		return $this->_tableClass;
	}

	/**
	 * Устанавливает опции для объекта таблицы.
	 *
	 * @param  $options
	 * @return Workset_Model_Object_Storage_Table_Abstract
	 */
	function setTableOptions(array $options) {
		$this->_tableOptions = $options;
		return $this;
	}

	/**
	 * Устанавливает объект таблицы для работы с БД.
	 *
	 * @param Workset_Db_Table $table
	 * @return Workset_Model_Object_Storage_Table_Abstract
	 */
	function setTable(Workset_Db_Table $table) {
		$this->_table = $table;
		return $this;
	}

	/**
	 * Возвращает объект таблицы для работы с БД.
	 *
	 * @return Workset_Db_Table
	 */
	function getTable() {
		return $this->_getTable();
	}

	/**
	 * Указывает, нужно ли подсчитывать общее кол-во записей (без limit/offset).
	 *
	 * @param  $flag
	 * @return Workset_Model_Object_Storage_Table_Abstract
	 */
    function setCountAllRecords($flag) {
        $this->_countAllRecords = (bool)$flag;
        return $this;
    }

	/**
	 * Создает новый объект модели.
	 *
	 * @throws Workset_Model_Exception
	 * @param  $data
	 * @return Workset_Model_Object_Abstract
	 */
	function create($data) {
		if (!$data instanceof Workset_Db_Table_Row) {
			$table = $this->_getTable();
			$data = $table->createRow($data);
		} else {
			$table = $data->getTable();
			if (!$table instanceof $this->_tableClass) {
				throw new Workset_Model_Exception('Invalid type of data row in '.get_class($this));
			}
		}

		return new $this->_objectClass($data, array(
			'storage' => $this
		));
	}

	/**
	 * Сохраняет объект модели в БД, возвращает ID сохраненного объекта.
	 *
	 * @throws Workset_Model_Exception
	 * @param Workset_Model_Object_Interface $object
	 * @param  $data
	 * @return int
	 */
	function save(Workset_Model_Object_Interface $object, $data = null) {
		$record = $object->getData();
		if ($record instanceof Workset_Db_Table_Row) {
			$table = $record->getTable();
			if ($table instanceof $this->_tableClass) {
				if (is_array($data)) {
					$record->setFromArray($data);
				}
				return $record->save();
			}
		}
		throw new Workset_Model_Exception('Invalid data type in object '.get_class($object));
	}

	function delete(Workset_Model_Object_Interface $object) {
		$record = $object->getData();
		if ($record instanceof Workset_Db_Table_Row) {
			$table = $record->getTable();
			if ($table instanceof $this->_tableClass) {
				return $record->delete();
			}
		}
		throw new Workset_Model_Exception('Invalid data type in object '.get_class($object));
	}

	function getOne($identity) {
		$table = $this->_getTable();
		$primary = $table->info('primary');
		if (!is_array($identity)) {
			if (count($primary) == 1) {
				$where = array(current($primary).' = ?' => $identity);
			}
		} else {
			if (array_diff($primary, array_keys($identity))) {
				throw new Workset_Model_Exception('Invalid identity columns in '.get_class($this));
			}
			$where = array();
			foreach ($primary as $key) {
				$where[$key.' = ?'] = $identity[$key];
				unset($identity[$key]);
			}

			//дополнительные поля(не primary)
			if (!empty($identity)) {
				$where = array_merge($where, $identity);
			}
		}

		if ($where) {
			$record = $table->fetchRow($where);
			if (null === $record) {
				if ($this->_throwNullPointerException) {
					throw new Workset_Model_Exception('Empty row has been returned in class '.get_class($this));
				} else {
					return null;
				}
			}

			return new $this->_objectClass($record, array(
				'storage' => $this
			));
		}
        
		throw new Workset_Model_Exception('Invalid identity condition in '.get_class($this));
	}

    function getAll($where = null, $order = null, $limit = null,
                    $offset = null, $offsetByPage = false, array $options = null) {
        $table = $this->_getTable();

        if (true == $offsetByPage) {
            $offset = $this->getPageOffset($limit, $offset);
        }

        if (null === $order) {
            $primary = $table->info('primary');
            $order = array();
            foreach ($primary as $key) {
               $order[] = $key.' DESC'; 
            }
        }

        if (0 >= $limit) {
            $limit = $this->_limit;
        }

        $rowset = $table->fetchAll($where, $order, $limit, $offset, $this->_countAllRecords);

        if (null === $options) {
            $options = array('filter' => $where);
        }

        return $this->getIterator($rowset, $options);

    }

    function getPageOffset($limit = null, $offset = null) {
        if ($limit > 0 && $offset > 0) {
            return ($limit * ($offset - 1));
        } else {
            return 0;
        }
    }

	function getCacheId($key = null) {
		$key = parent::getCacheId($key);
		$key .= $this->getTableClass();

		return $key;
	}

    function beginTransaction() {
        $table = $this->_getTable();
        return $table->getAdapter()->beginTransaction();
    }

    function commit() {
        $table = $this->_getTable();
        return $table->getAdapter()->commit();
    }

    function rollBack() {
        $table = $this->_getTable();
        return $table->getAdapter()->rollBack();
    }

    function getIterator($rowset, $options = null) {

        $iterator = parent::getIterator($rowset, $options);

        $cnt = isset($options['countRows']) ? (boolean)$options['countRows'] : $this->_countAllRecords;

        if ($cnt) {
            $table = $this->_getTable();

            if ($this->_countMethod == 'calc_found_rows') {
                $total = $table->getAdapter()
                        ->query(Workset_Db_Table_Select::SQL_SELECT.' '.Workset_Db_Table_Select::FOUND_ROWS.' as cnt')
                        ->fetchColumn();

            } elseif ($this->_countMethod == 'select_count') {
                $where = isset($options['filter']) ? $options['filter'] : null;

                if (!$where instanceof Zend_Db_Table_Select) {
                    $select = $table->prepareSelect($where);
                    $select->from($table, array('cnt' => new Zend_Db_Expr('COUNT(*)')));
                } else {
                    $select = $where;
                    $select->reset(Zend_Db_Table_Select::COLUMNS);
                    $select->reset(Zend_Db_Table_Select::ORDER);
                    $select->reset(Zend_Db_Table_Select::LIMIT_OFFSET);
                    $select->reset(Zend_Db_Table_Select::LIMIT_COUNT);
                    $select->columns(array('cnt' => new Zend_Db_Expr('COUNT(*)')));
                }

                $total = $table->getAdapter()->query($select)->fetchColumn();

            } else {
                throw new Workset_Model_Exception('Undefined rows count type, set "calc_found_rows" or "select_count" method');
            }

            $iterator->setTotalCount($total);
        }

        return $iterator;
    }

    protected function _getTable($class = null) {
		if (is_string($class)) {
			return Workset_Db_Table::getStaticTable($class);
		} elseif (null === $this->_table) {
			$this->_table = Workset_Db_Table::getStaticTable($this->_tableClass, $this->_tableOptions);
		}
		return $this->_table;
	}



}

?>